﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AForge.Genetic;
using AForge.Math.Random;
using AForge;

namespace dto
{
    public class myDoubleArrayChromosome : ChromosomeBase
    {
         protected IRandomNumberGenerator chromosomeGenerator;
         protected IRandomNumberGenerator mutationMultiplierGenerator;
         protected IRandomNumberGenerator mutationAdditionGenerator;
         protected static Random rand = new Random( );
         
       public const int MaxLength = 65536;
       private int length;
       protected double[] val = null;
       private double mutationBalancer = 0.5;
        private double crossoverBalancer = 0.5;

        public int Length
        {
            get { return length; }
        }

      
        public double[] Value
        {
            get { return val; }
        }

        public double MutationBalancer
        {
            get { return mutationBalancer; }
            set { mutationBalancer = Math.Max( 0.0, Math.Min( 1.0, value ) ); }
        }

      
        public double CrossoverBalancer
        {
            get { return crossoverBalancer; }
            set { crossoverBalancer = Math.Max( 0.0, Math.Min( 1.0, value ) ); }
        }

       
        public myDoubleArrayChromosome(
            IRandomNumberGenerator chromosomeGenerator,
            IRandomNumberGenerator mutationMultiplierGenerator,
            IRandomNumberGenerator mutationAdditionGenerator,
            int length )
        {

            // save parameters
            this.chromosomeGenerator = chromosomeGenerator;
            this.mutationMultiplierGenerator = mutationMultiplierGenerator;
            this.mutationAdditionGenerator = mutationAdditionGenerator;
            this.length = Math.Max( 2, Math.Min( MaxLength, length ) ); ;

            // allocate array
            val = new double[length];

            // generate random chromosome
            Generate( );
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DoubleArrayChromosome"/> class.
        /// </summary>
        /// 
        /// <param name="chromosomeGenerator">Chromosome generator - random number generator, which is 
        /// used to initialize chromosome's genes, which is done by calling <see cref="Generate"/> method
        /// or in class constructor.</param>
        /// <param name="mutationMultiplierGenerator">Mutation multiplier generator - random number
        /// generator, which is used to generate random multiplier values, which are used to
        /// multiply chromosome's genes during mutation.</param>
        /// <param name="mutationAdditionGenerator">Mutation addition generator - random number
        /// generator, which is used to generate random addition values, which are used to
        /// add to chromosome's genes during mutation.</param>
        /// <param name="values">Values used to initialize the chromosome.</param>
        /// 
        /// <remarks><para>The constructor initializes the new chromosome with specified <paramref name="values">values</paramref>.
        /// </para></remarks>
        /// 
        /// <exception cref="ArgumentOutOfRangeException">Invalid length of values array.</exception>
        /// 
        public myDoubleArrayChromosome(
            IRandomNumberGenerator chromosomeGenerator,
            IRandomNumberGenerator mutationMultiplierGenerator,
            IRandomNumberGenerator mutationAdditionGenerator,
            double[] values )
        {
            if ( ( values.Length < 2 ) || ( values.Length > MaxLength ) )
                throw new ArgumentOutOfRangeException( "Invalid length of values array." );

          // save parameters
            this.chromosomeGenerator = chromosomeGenerator;
            this.mutationMultiplierGenerator = mutationMultiplierGenerator;
            this.mutationAdditionGenerator = mutationAdditionGenerator;
            this.length = values.Length;

            // copy specified values
            val = (double[]) values.Clone( );
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DoubleArrayChromosome"/> class.
        /// </summary>
        /// 
        /// <param name="source">Source chromosome to copy.</param>
        /// 
        /// <remarks><para>This is a copy constructor, which creates the exact copy
        /// of specified chromosome.</para></remarks>
        /// 
        public myDoubleArrayChromosome( myDoubleArrayChromosome source )
        {
            this.chromosomeGenerator = source.chromosomeGenerator;
            this.mutationMultiplierGenerator = source.mutationMultiplierGenerator;
            this.mutationAdditionGenerator = source.mutationAdditionGenerator;
            this.length  = source.length;
            this.fitness = source.fitness;
            this.mutationBalancer = source.mutationBalancer;
            this.crossoverBalancer = source.crossoverBalancer;

            // copy genes
            val = (double[]) source.val.Clone( );
        }

        /// <summary>
        /// Get string representation of the chromosome.
        /// </summary>
        /// 
        /// <returns>Returns string representation of the chromosome.</returns>
        /// 
        public override string ToString( )
        {
            StringBuilder sb = new StringBuilder( );

            // append first gene
            sb.Append( val[0] );
            // append all other genes
            for ( int i = 1; i < length; i++ )
            {
                sb.Append( ' ' );
                sb.Append( val[i] );
            }

            return sb.ToString( );
        }

        /// <summary>
        /// Generate random chromosome value.
        /// </summary>
        /// 
        /// <remarks><para>Regenerates chromosome's value using random number generator.</para>
        /// </remarks>
        ///
        public override void Generate( )
        {
            for ( int i = 0; i < length; i++ )
            {
                // generate next value
                val[i] = chromosomeGenerator.Next( );
            }
        }

        /// <summary>
        /// Create new random chromosome with same parameters (factory method).
        /// </summary>
        /// 
        /// <remarks><para>The method creates new chromosome of the same type, but randomly
        /// initialized. The method is useful as factory method for those classes, which work
        /// with chromosome's interface, but not with particular chromosome type.</para></remarks>
        ///
        public override IChromosome CreateNew( )
        {
            return new myDoubleArrayChromosome( chromosomeGenerator, mutationMultiplierGenerator, mutationAdditionGenerator, length );
        }

        /// <summary>
        /// Clone the chromosome.
        /// </summary>
        /// 
        /// <returns>Return's clone of the chromosome.</returns>
        /// 
        /// <remarks><para>The method clones the chromosome returning the exact copy of it.</para>
        /// </remarks>
        ///
        public override IChromosome Clone( )
        {
            return new myDoubleArrayChromosome( this );
        }

        public override void Mutate( )
        {
            int mutationGene = rand.Next( length );

            if ( rand.NextDouble( ) < mutationBalancer )
            {
                val[mutationGene] *= mutationMultiplierGenerator.Next( );
            }
            else
            {
                val[mutationGene] += mutationAdditionGenerator.Next( );
            }
        }
        
        public override void Crossover(IChromosome pair)
        {
            myDoubleArrayChromosome p = (myDoubleArrayChromosome)pair;
            if ((p != null) && (p.Length == Length))
            {
                switch (DrSommer.GetInstance().HowToDoIt)
                {
                    case dto.Crossover.OnePoint:
                        {
                            int crossOverPoint = rand.Next(this.Length - 1) + 1;
                            // length of chromosome to be crossed
                            int crossOverLength = Length - crossOverPoint;
                            // temporary array
                            double[] temp = new double[crossOverLength];

                            // copy part of first (this) chromosome to temp
                            Array.Copy(val, crossOverPoint, temp, 0, crossOverLength);
                            // copy part of second (pair) chromosome to the first
                            Array.Copy(p.val, crossOverPoint, val, crossOverPoint, crossOverLength);
                            // copy temp to the second
                            Array.Copy(temp, 0, p.val, crossOverPoint, crossOverLength);
                        }
                    break;
                    case dto.Crossover.Template: 
                        {
                             double temp = 0;

                            
                              for (int crossOverPoint = 0;crossOverPoint<val.Length;crossOverPoint++ )
                              {

                                  if ( DrSommer.GetInstance().Template[(crossOverPoint % (DrSommer.GetInstance().Template.Length))]== '1')
                                  {
                                      temp = p.val[crossOverPoint];
                                      p.val[crossOverPoint] = val[crossOverPoint];
                                      val[crossOverPoint] = temp;
                                  }
                                  crossOverPoint++;
                              
                              }
                        }
                           
                        
                    break;
                    case dto.Crossover.TwoPoint: 
                        {
                            int crossOverPoint1 = rand.Next(this.Length - 2) + 1;
                            // length of chromosome to be crossed
                            int crossOverPoint2 = rand.Next(crossOverPoint1+1,this.Length);
                            // temporary array
                            double[] temp = new double[(crossOverPoint2-crossOverPoint1)+1];

                            // copy part of first (this) chromosome to temp
                            Array.Copy(val, crossOverPoint1, temp, 0, (crossOverPoint2 - crossOverPoint1) + 1);
                            // copy part of second (pair) chromosome to the first
                            Array.Copy(p.val, crossOverPoint1, val, crossOverPoint1, (crossOverPoint2 - crossOverPoint1) + 1);
                            // copy temp to the second
                            Array.Copy(temp, 0, p.val, crossOverPoint1, (crossOverPoint2 - crossOverPoint1) + 1);
                        }
                    break;

                }
            }
        }

       
    }
}
